class sale_order(osv.osv):
    _name = "sale.order"
    _inherit = ['mail.thread','ir.needaction_mixin']
    _description = "Sales Order"
    _track = {
        'state':{
            'sale.mt_order_confirmed':lambda self, cr, uid, obj, ctx=None: obj.state in ['manual'],
            'sale.mt_order_sent':lambda self, cr, uid, obj, ctx= None: obj.state in ['sent']
            
        },
    }
    
    def _amout_line_tax(self, cr ,uid, line, context=None):
        val =0.0
        for c in self.pool.get('account.tax').compute_all(cr, uid, line.tax_id, line.price_unit*(1-(line.discount or 0.0)/100.0),line.product_uom_qty, line.product_id, line.order_id.partner_id)['taxes']:
            val +=c.get('amount',0,0)
        return val
        
    
    def _amount_all_wrapper(self, cr, uid, ids, field_name, context=None):
        return self._amount_all(cr,uid,ids, field_name,arg, context=context)
    
    def _amount_all(self, cr ,uid, ids, field_name, arg, context=None):
        cur_obj= self.pool.get('res.currency')
        res = {}
        for order in self.browse(cr, uid, ids, context):
            res[order.id] = {
                'amount_untaxed':0.0,
                'amount_tax':0.0,
                'amount_total':0.0,
            }
            val = val1 =0.0
            cur = order.pricelist_id.currency_id
            for line in order.order_line:
                val1 += line.price_subtotal
                val += self._amount_line_tax(cr ,uid, line, context = context)
            res[order.id]['amount_tax'] = cur_obj.round(cr, uid, cur ,val)
            res[order.id]['amount_untaxed'] = cur_obj.round(cr,uid, cur, val1)
            res[order.id]['amount_total'] = res[order.id]['amount_untaxed'] + res[order.id]['amount_tax']
        return res
        
    
    def _invoiced_rate(self, cursor, user, ids, name, arg, context=None):
        res = {}
        for sale in self.browse(cursor, user, ids, context= context):
            if sale.invoiced:
                res[sale.id] = 100.0
                continue
            tot = 0.0
            for invoice in sale.invoice_ids:
                if invoice.state not in ('draft', 'cancel'):
                    tot += invoice.amount_untaxed
            
            if tot:
                res[sale.id] = min(100.0, tot*100.0/(sale.amount_untaxed or 1.00))
            else:
                res[sale.id] = 0.0
            
        return res
        
    def _invoice_exists(self, cursor, user, ids, name, arg, context=None):
        res ={}
        for sale in self.browse(cursor, user, ids, context=context):
            rse[sale.id] = False
            if sale.invoice_ids:
                res[sale.id] = True
        return res
        
    def _invoiced(self, cursor, user, ids, name, arg, context=None):
        res = {}
        for sale in  self.browser(cursor, user, ids, context=contex):
            res[sale.id] = True
            invoice_existence = False
            for voice in sale.Invoice_ids:
                if invoice.state!='cancel':
                    invoice_existence = True
                    if invoice.state !='paid':
                        res[sale.id] = False
                        break
            if not invoice_existence or sale.state =='manual':
                res[sale.id]=False
        return res
        
        
    def _invoice_search(self, cursor, user, obj, name, args, context=context):
        if not len(args):
            return []
        clause = ''
        sale_clause = ''
        no_invoiced = False
        for arg in args:
            if (arg[1] =='=' and arg[2] or (arg[1] =='!=' and not arg[2])):
                clause += 'And inv.state = \'paid\''
            else:
                clause += 'AND inv.state !=\'cancel\' AND sale.state!=\'cancel\' AND inv.state<>\'paid\' AND rel.order_id=sale.id '
                sale_clause =', sale_order AS sale'
                no_invoiced = True
        
        cursor.execute('select rel.order_id' \ 
                'from sale_order_invoice_rel as rel, account_invoice as inv' + sale_clause +\
                'where rel.invoice_id = inv.id' + clause)
            
        res = cursor.fetchall()
        if no_invoiced:
            cursor.execute(
            'select sale.id form sale_order as sale where sale.id not in (selcet rel.order_id from sale_order_invoice_res as rel ) and sale.state !=\'cancel\' '
            )
            res.extend(cursor.fetchall())
        
        if not res:
            return [('id','=',0)]
        return [('id','in', [x[0] for x in res])]
        
    def _get_order(self, cr, uid, ids, context=None):
        result = {}
        for line in self.pool.get('sale.order.line').browse(cr,uid, ids, context = context):
            result[line.order_id.id] = True
        return result.leys()
        
    def _get_order(self, cr ,uid, ids, context=None):
        result = {}
        for line in self.pool.get('sale.order.line').browse(cr,uid, ids, context= context):
            result[line.order_id.id] = True
        return result.keys()
        
    
    def _get_default_company(self, cr , uid, context = None):
        company_id = self.pool.get('res.users')._get_company(cr, uid, context=context)
        if not company_id:
            raise osv.except_osv(_('ERROR!'),_('There is no defult company for the current user!'))
        return company_id
        
    
    def _get_default_selection_id(self, cr ,uid, context = None):
        selection_id = self._resolve_selection_id_from_context(cr, uid, context=context) or False
        if not selection_id:
            selection_id = self.pool.get('res.users').browse(cr, uid, uid, context).default_selection_id or False
        return selection_id
        
    def _resolve_selection_id_from_context(self, cr, uid, context=None):
        if context is None:
            context = {}
        if type(context.get('default_selection_id')) in (int, long):
            return context.get('default_selection_id')
        if isinstance(context.get('default_selection_id'), basestring):
            selection_ids = self.pool.get('crm.case.selection').name_search(cr,uid, name=context['default_selection_id'], context=context)
            if len(selection_ids) ==1:
                return int(selection_ids[0][0])
        return None
        
    _columns = {
        'name': field.char('Order Reference', required=True, copy=False, readonly=True, state={'draft':[('readonly',False)], 'sent':[('readonly':False)]},selectid=True),
        'origin':fields.char('Source Document', help='Reference of the document that generated this sales order requese.'),
        'client_order_ref':fields.char('reference/Description',copy=False),
        'state':fields.selection([
            ('draft;,'Draft Quotation'),
            ('sent', 'Quotation sent'),
            ('cancel', 'Cancelled'),
            ('waiting_date','waiting schedule'),
            ('progress','sale order'),
            ('manual','sale to invoice'),
            ('shipping_except','shipping exception'),
            ('invoice_except','Incoice Exception'),
            ('done','Done'),
        ],'status',readonly=True, copy=False, help='Gives the status of the quotation or sales order\
              \nThe exception status is automatically set when a cancel operation occurs \
              in the invoice validation (Invoice Exception) or in the picking list process (Shipping Exception).\nThe 'Waiting Schedule' status is set when the invoice is confirmed\
               but waiting for the scheduler to run on the order date.", select=True),
        'date_order': fields.datetime('Date', required=True, readonly=True, select=True, states={'draft': [('readonly', False)], 'sent': [('readonly', False)]}, copy=False),
        'create_date': fields.datetime('Creation Date', readonly=True, select=True, help="Date on which sales order is created."),
        'date_confirm': fields.date('Confirmation Date', readonly=True, select=True, help="Date on which sales order is confirmed.", copy=False),
        'user_id': fields.many2one('res.users', 'Salesperson', states={'draft': [('readonly', False)], 'sent': [('readonly', False)]}, select=True, track_visibility='onchange'),
        'partner_id': fields.many2one('res.partner', 'Customer', readonly=True, states={'draft': [('readonly', False)], 'sent': [('readonly', False)]}, required=True, change_default=True, select=True, track_visibility='always'),
        'partner_invoice_id': fields.many2one('res.partner', 'Invoice Address', readonly=True, required=True, states={'draft': [('readonly', False)], 'sent': [('readonly', False)]}, help="Invoice address for current sales order."),
        'partner_shipping_id': fields.many2one('res.partner', 'Delivery Address', readonly=True, required=True, states={'draft': [('readonly', False)], 'sent': [('readonly', False)]}, help="Delivery address for current sales order."),
        'order_policy': fields.selection([
                ('manual', 'On Demand'),
            ], 'Create Invoice', required=True, readonly=True, states={'draft': [('readonly', False)], 'sent': [('readonly', False)]},
            help="""This field controls how invoice and delivery operations are synchronized."""),
            
            'pricelist_id':field.many2one('product.pricelist','Pricelist',required=True, readonly=True, states = {'draft':[('readonly',False)],'sent':[('readonly',False)]}, help="pricelist for current sales order!")
            'currency_id':fields.related('pricelist_id','currency_id', type='many2one', relation='res.currency', string = 'currency', readonly=True,required=True),
            'project_id':fields.many2one('account.analytic.account', 'Contract/Analytic', readonly=True, status={'draft':[('readonly':False)],'sent':[('readonly',False)]},help="The Analytic account related to a sales order."),
            'order_line': fields.one2many('sale.order.line','order_id','order lines', readonly=True, states={'draft':[('readonly', False)], 'sent':[('readonly':False)]}, copy=True),
            'invoice_ids':fields.many2many('account.invoice', 'sale_order_invoice_rel', 'order_id', 'invoice_ids', 'Invoices', readonly=True, copy=False, help="this is the list of invoice that have been generated for this sales order, the same sales order may have been invoiced in several times(by line for example)."),
            'invoiced_rate':fields.function(_invoiced_rate, string='Inviced Ratio', type='float'),
            'invoiced':fields.function(_invoiced_rate, string='Paid', fnct_search=_invoiced_search, type='boolean', help="It indicates that an invoice has been paid."),
            'invoiced_exists':fields.function(_invoiced_exists, string='Invoiced', fnct_search=_invoiced_search, type='boolean', help='it indicates that sales order has at lease on invoice.')
            'note':fields.text('Terms and conditions'),
            'amount_untaxed':fields.function(_amount_all_wrapper, digits_compute = dp.get_precision('Account'), string='Untaxed Amount',
                store ={
                    'sale.order':(lambda self, cr ,uid, ids, c={}, ['order_line'],10),
                    'sale.order.line':(_get_order, ['price_unit','tax_id','discount','product_uom_qty'],10),
                },
                multi='sums', help ="the amount without tax.", track_visibility='always'
            ),
            'amount_tax': fields.function(_amount_all_wrapper, digits_compute= dp.get_precision('Account'), string='Taxes',
                store={
                    'sale.order':(lambda self, cr, uid, ids, c={}: ids,['order_line'],10),
                    'sale.order.line':(_get_order,['price_unit','tax_id','discount','product_uom_qty'],10),
                },
                multi='sums', help="the tax amount."
            ),
            'amount_total':field.function(_amount_all_wrapper, digits_compute=dp.get_precision('Account'), string='Total',
                store={
                    'sale.order':(lambda self, cr, uid, ids, c={} : ids,['order_line'],10),
                    'sale.order.line':(_get_order, ['price_unit',' tax_id', 'discount', 'product_uom_qty'],10),
                },
                multi='sums', help="the total amount."
            ),
            
            'payment_term':fields.many2one('account.payment.term', 'Payment Term'),
            'fiscal_position':fields.many2one('account.fiscal.position', 'Fiscal Position'),
            'company_id':fields.many2one('res.company','company'),
            'section_id':fields.many2one('crm.case.section','Sales Team'),
            'procurement_group_id':field.many2one('procurement.group','Procurement group', copy=False),
            'Product_id':fields.related('order_line','product_id', type='many2one', relation='product.product', string = 'Product'),
        
    }
    
    _defaults = {
        'date_order': fields.datetime.now,
        'order_policy':'manual',
        'company_id':_get_default_company,
        'state':'draft',
        'user_id':lambda obj, cr ,uid, context:uid,
        'name':lambda obj,cr, uid, context:'/'
        'partner_invoice_id': lambda self, cr, uid, context: context.get('partner_id', False) and self.pool.get('res.partner').address_get(cr,uid, [context['partner_id']],['invoice'])['invoice'],
        'partner_shipping_id':lambda self, cr, uid, context:context.get('partner_id',False) and self.pool.get('res.partner').address_get(cr, uid, [context['partner_id']], [delivery])['delivery'],
        'selection_id':lambda s, cr ,uid, c:s._get_default_section_id(cr, uid, c),
    }
    _sql_constraints =[
        ('name_uniq', 'unique(name, company_id)', 'Order Reference must be unique per Company!'),
    ]
    
    _order = 'date_order desc, id desc'
    
    
    #Form filling
    def unlink(self, cr ,uid, ids, context = None):
        sale_orders = self.read(cr, uid, ids, ['state'], context=context)
        unlink_ids = []
        for s in sale_orders:
            if s['state'] in ['draft','cancel']:
                unlink_ids.append(s['id'])
            else:
                raise osv.except_osv(_('Invalid Action'),_('In order to delete a confirmed sales order, you must cancel it before!'))
            
        return osv.osv.unlink(self, cr ,uid, unlink_ids, context=context)
        
    
    def copy_quotation(self, cr , uid, ids , context=None):
        id = self.copy(cr, uid, ids[0], context=context)
        view_ref = self.pool.get('ir.model.data').get_object_reference(cr, uid, 'sale','view_order_form')
        view_id = view_ref and view_ref[1] or False,
        return{
            'type':'ir.actions.act_window',
            'name':_('Sale Order'),
            'res_id':id,
            'view_mode':'form',
            'view_type':'form',
            'view_id':view_id,
            'target':'current',
            'nodestroy':True,
        }
    
    def onchange_priceList_id(self,cr,uid,ids,pricelist_id, orger_lines, context=None):
        context = context or {}
        if not pricelist_id:
            return {}
        value = {
            'currency_id': self.pool.get('product.pricelist').browse(cr, uid, pricelist_ld, context=context).currency_id.id
        }
        if not order_lines or order_lines ==[{6,0,[]}]:
            return {'value':value}
        
        warning = {
            'title':_('Pricelist Warning!'),
            'message':_('If you change the pricelist of this order (and eventually the currency), price of existing order line will not be updated.')
        }
        return {'warning':Warning, 'value':value}
        
    
    def get_salenote(self, cr, uid,ids, partner_id, context=None):
        context_lang = context.copy()
        if partner_id:
            partner_lang = self.pool.get('res.partner').browse(cr,uid,partner_id,context=context).lang
            context_lang.update({'lang':partner_lang})
        return self.pool.get('res.users').browse(cr,uid, uid, context=context_lang).company_id.sale_note
        
    def onchange_delivery_id(self, cr, uid, ids, company_id, partner_id, delivery_id, fiscal_position, context=None):
        r = {'value':{}}
        if not fiscal_position:
            company_id = self._get_default_company(cr, uid, context = context)
        fiscal_position = self.pool['account.fiscal.position'].get_fiscal_position(cr,uid, company_id, partner_id, delivery_id, context=context)
        if fiscal_position:
            r['value']['fiscal_position'] = fiscal_position
        return r
        
    def onchange_partner_id(self, cr ,uid, ids, part, context=None):
        if not part:
            return {'value':{'partner_invoice_id':False, 'partner_shipping_id':False, 'payment_term':False, 'fiscal_position':False}}
            
        part = self.pool.get('res.partner').browse(cr,uid,part, context=context)
        addr = self.pool.get('res.partner').address_get(cr, uid, [part.id],['delivery','invoice','contact'])
        pricelist = part.property_product_pricelist and part.property_product_pricelist.id or False
        payment_term = part.property_payment_term and part.property_payment_term or False
        dedicated_salesman = part.user_id and part.user_id.id or uid
        val = {
            'partner_invoice_id':addr['invoice'],
            'partner_shipping_id':addr['delivery'],
            'payment_term':payment_term,
            'user_id':dedicated_salesman,
        }
        delivery_onchange = self.onchange_delivery_id(cr, uid, ids, False, part.id, addr['delivery'], False, context = context)
        val.update(delivery_onchange['value'])
        if pricelist:
            val['pricelist_id'] = pricelist
        sale_note = self.get_salenote(cr, uid, ids, part.id, context=context)
        if sale_note: val.update({'note',sale_note})
        return {'value':val}
        
        
        
        
            
            
            
            
            
            
            
            
            
            
            
            
            
            
            
            
            
            
            
            
            
            
            
            
            
            
            
            
            
            
            
            
            
            
            
            
            
            
            
            
            
            
            
            
            
            
            
            